#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <cassert>
#include <unordered_map>
#include "../source/server.hpp"
#include "Util.hpp"
#include "Req_Resp.hpp"
#include "Context.hpp"

#define DEFALT_TIMEOUT 10
using Handler = std::function<void(const HttpRequest &, HttpResponse *)>;
using Handlers = std::vector<std::pair<std::regex, Handler>>;
class HttpServer
{
public:
    HttpServer(int port, int timeout = DEFALT_TIMEOUT):_server(port) {
            _server.EnableInactiveRelease(timeout);
            _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
            _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
        }
    //设置静态资源根路径
    void SetBaseDir(const std::string &path)
    {
        assert(Util::IsDirectory(path) == true);
        _basedir = path;
    }
    void Get(const std::string &pattern, const Handler &handler)
    {
        _get_route.push_back(std::make_pair(std::regex(pattern), handler));
    }
    void Post(const std::string &pattern, const Handler &handler)
    {
        _post_route.push_back(std::make_pair(std::regex(pattern), handler));
    }
    void Put(const std::string &pattern, const Handler &handler)
    {
        _put_route.push_back(std::make_pair(std::regex(pattern), handler));
    }
    void Delete(const std::string &pattern, const Handler &handler)
    {
        _delete_route.push_back(std::make_pair(std::regex(pattern), handler));
    }
    void SetThreadCount(int count)
    {
        _server.SetThreadCount(count);
    }
    void Listen()
    {
        _server.Start();
    }

private:
    // 将HttpResponse的要素按照http协议格式进行组织发送
    void WriteResponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse &resp)
    {
        //1.完善头部字段
        //(检查是否设置了短连接)
        if(resp.Close() == true)
            resp.SetHeader("Connection","close");
        else 
            resp.SetHeader("Connection","keep-alive");
        if(resp._body.empty() == false && resp.HasHeader("Content-Length") == false)
            resp.SetHeader("Content-Length",std::to_string(resp._body.size()));//正文长度
        if(resp._body.empty() == false && resp.HasHeader("Content-Type") == false)
            resp.SetHeader("Content-Type","application/octet-stream");//编码格式
        if(resp._redirect_flag == true)
            resp.SetHeader("Location",resp._redirect_url);//重定向类型属性(hash,host,hostname....)
        //2.将resp中的要素，按照http格式进行组织": "隔开
        std::stringstream resp_str;
        resp_str << req._version << " " << std::to_string(resp._statu) << " " << Util::StatuDesc(resp._statu) << "\r\n";
        for(auto &head : resp._headers)
        {
            resp_str << head.first << ": " << head.second << "\r\n";
        }
        resp_str << "\r\n";
        resp_str << resp._body;
        //3.发送数据
        conn->Send(resp_str.str().c_str(),resp_str.str().size());
    }
    //判断一个请求是不是静态资源请求
    bool IsFileHandler(const HttpRequest &req)
    {
        //1.必须设置了静态资源的根目录
        if(_basedir.empty())
            return false;
        //2.请求方法必须是GET 或者 HEAD
        if(req._method != "GET" || req._method != "HEAD")
            return false;
        //3.请求路径必须合法
        if(Util::ValidPath(req._path) == false)
            return false;
        //4.请求的资源也必须存在,且是一个普通文件
        //如果请求的是静态资源的根路径，也就是没有请求具体的文件，那么我们统一添加一个index.html
        std::string req_path = _basedir + req._path;//为了避免直接修改请求的资源路径，因此定义一个临时对象
        if(req_path.back() == '/')
            req_path += "index.html";
        if(Util::IsRegular(req_path) == false)
            return false;
        return true;
    }
    // 静态资源的请求处理(把静态资源文件的数据读取出来，放到rsp的_body中, 并设置mime)
    void FileHandler(const HttpRequest& req, HttpResponse* resp)
    {
        std::string req_path = _basedir + req._path;
        if(req._path.back() == '/')
            req_path += "index.html";
        bool ret = Util::ReadFile(req_path,&resp->_body);
        if(ret == false)
            return;
        std::string mime = Util::ExtMime(req_path);
        resp->SetHeader("Content-Type", mime);
        return ;
    }
    /**
     * 功能性请求的处理
    */
    void Dispatcher(HttpRequest &req, HttpResponse *resp, Handlers &handlers)
    {
        for(auto &handle : handlers)
        {
            const std::regex &re = handle.first;
            const Handler &functor = handle.second;
            /**
             * 参数说明std::regex_match()
             * 1.待处理的字符串
             * 2.将处理后的字符串放到该变量中
             * 3.处理字符串规则
            */
            bool ret = std::regex_match(req._path,req._matches,re);
            if(ret == false)
                continue;
            //传入请求信息，和空的resp，执行处理函数
            return functor(req,resp);
        }
        resp->_statu = 404;
    }
    // 开始路由
    /**
     * 需要对请求的资源进行分辨，分辨出是静态资源请求还是功能行资源请求
     * 如果是静态资源请求，就调用静态资源请求接口
     * 如果是功能性请求，那么就先分辨出是哪种功能请求
     * 最后既不是静态请求也不是功能性请求，就返回对应的statu(405)
     */
    void Route(HttpRequest &req, HttpResponse *resp)
    {
        if (IsFileHandler(req) == true)
            return FileHandler(req, resp);
        if (req._method == "GET" || req._method == "HEAD")
            return Dispatcher(req, resp, _get_route);
        else if (req._method == "POST")
            return Dispatcher(req, resp, _post_route);
        else if (req._method == "PUT")
            return Dispatcher(req, resp, _put_route);
        else if (req._method == "DELETE")
            return Dispatcher(req, resp, _delete_route);
        resp->_statu = 405; // Method Not Allowed
        return ;
    }
    // 设置上下文
    void OnConnected(const PtrConnection &conn)
    {
        conn->SetContext(HttpContext());
        logMessage(FATAL, "new connection:%p", conn.get());
    }
    // 缓冲区数据解析和处理
    void OnMessage(const PtrConnection &conn, Buffer *buffer)
    {
        while (buffer->ReadAbleSize() > 0)
        {
            // 1.获取上下文
            HttpContext *context = conn->GetContext()->get<HttpContext>();
            // 2.通过上下文对缓冲区数据进行解析,得到HttpRequest对象
            // 2.1 如果缓冲区的数据解析出错了，就直接回复出错响应
            // 2.2 如果解析正常，且请求已经获取完毕，才开始去进行处理
            context->RecvHttpRequest(buffer);
            HttpRequest &req = context->Request();
            HttpResponse resp(context->RespStatu());
            // 响应出错时
            if (context->RespStatu() >= 400)
            {
                ErrorHandler(req, &resp);       // 填充一个错误显示页面数据到resp
                WriteResponse(conn, req, resp); // 组织响应发送给客户端
                context->ReSet();               // 对上下文进行重置
                // 出错了就把缓冲区的数据清空
                buffer->MoveReadOffest(buffer->ReadAbleSize());
                conn->ShutDown(); // 关闭连接
                return;
            }
            // 当前请求还没有接收完整，退出，等待数据完整
            if (context->RecvStatu() != RECV_HTTP_OVER)
            {
                return;
            }
            // 3.请求路由+业务处理
            Route(req, &resp);
            // 4.对HttpResponse进行组织发送
            WriteResponse(conn, req, resp);
            // 5.重置上下文
            context->ReSet();
            // 6.根据长短连接判断是否关闭连接或者继续处理
            if (resp.Close() == true)
                conn->ShutDown(); // 短连接直接关闭
        }
        return;
    }
    //错误
    void ErrorHandler(const HttpRequest &req, HttpResponse *resp)
    {
        // 1. 组织一个错误展示页面
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body += "</head>";
        body += "<body>";
        body += "<h1>";
        body += std::to_string(resp->_statu);
        body += " ";
        body += Util::StatuDesc(resp->_statu);
        body += "</h1>";
        body += "</body>";
        body += "</html>";
        // 2. 将页面数据，当作响应正文，放入rsp中
        resp->SetContent(body, "text/html");
    }
private:
    // std::unordered_map<std::string, Handler> _get_route;
    // std::unordered_map<std::string, Handler> _post_route;
    // std::unordered_map<std::string, Handler> _put_route;
    // std::unordered_map<std::string, Handler> _delete_route;
    Handlers _get_route;
    Handlers _post_route;
    Handlers _put_route;
    Handlers _delete_route;
    std::string _basedir; // 静态资源根目录
    TcpServer _server;
};